package Operations;

import java.lang.StringBuffer;
import java.util.Random;

import util.Filter;
import util.GAConfig;

public class ValuesGenerator {



	  /**
	   * The random number used any testtime a random choice is made. (Developer note:
	   * do not declare new Random objects; use this one instead).
	   */
	public  static Random random;// = new Random(GAConfig.RANDOM_SEED);

	 public static void resetSeed(long newSeed){
	    random = new Random();
	    random.setSeed(newSeed);
	  }

	public static int getRandomInt( int max) {
//		System.out.println("GAConfig.RANDOM_SEED"+GAConfig.RANDOM_SEED);
		int randomNumber= random.nextInt(max);
		return randomNumber;
		
	}
	public static int getRandomIntBetween(int min, int max) {
	
		long range       = (long)max - (long)min + 1;
		long fraction    = (long)(range * random.nextDouble());
	    int randomNumber = (int)(fraction + min);
		return randomNumber;
	}

	public static float getRandomFloat() {
		return random.nextFloat();
	}

	public static double getRandomDouble() {
		return random.nextDouble();
	}

	public static boolean getBoleean(float prob){
		return random.nextFloat() < prob;
	}
	public static String buildValue(String type) {

		if (type.startsWith("java.lang.Integer")||type.startsWith("int"))
			return buildIntValue();
		else 
			if (type.toLowerCase().contains("float"))
				return buildFloatValue(type);
			else if(type.toLowerCase().contains("double")) 
				return buildDoubleValue(type);
			else
				if (type.toLowerCase().contains("boolean"))
					return buildBoolValue(type);
				else
					if(type.toLowerCase().contains("char"))
						return buildCharacterValue();
					else
						if (type.startsWith("java.lang.long")||type.startsWith("long"))
							return buildLongValue();
						else
							return buildStringValue(type);
	}

	public static String modifyValue(String type,String sugesstedValue) {
		Object valueObj;
		if (type.startsWith("java.lang.Integer")||type.startsWith("int"))
			 valueObj=mapValueToObject(java.lang.Integer.class,sugesstedValue);
		else 
			if (type.startsWith("float") ||type.startsWith("java.lang.Float")) 
				 valueObj=mapValueToObject(java.lang.Float.class,sugesstedValue);
			else
				if (type.startsWith("double")|| type.startsWith("java.lang.Double"))
						valueObj=mapValueToObject(java.lang.Double.class,sugesstedValue);
			else
				if (type.startsWith("boolean"))
					 valueObj=mapValueToObject(java.lang.Boolean.class,sugesstedValue);
				else
					if (type.startsWith("java.lang.Long")||type.startsWith("long"))
						 valueObj=mapValueToObject(java.lang.Long.class,sugesstedValue);
					else
					 valueObj=mapValueToObject(java.lang.String.class,sugesstedValue);
		if(valueObj!=null)
			return sugesstedValue;
	return null;
	
	}
	
	
	public static String buildBoolValue(String type) {
		int n = getRandomInt(100);
		if (n < 50)
			return "true";
		else
			return "false";
	}
	public static String buildIntValue() {
		float nextFloat = random.nextFloat();
		int n=0;
		if(nextFloat < Operation.GEN_BASIC_VALUES){
			switch(random.nextInt(5)) {
				case 0:
					n= 0;
					break;
				case 1:
					n= 1;break;
				case 2:
					n= -1;break;
				case 3:
					n= Integer.MAX_VALUE;break;
				case 4:
					n= Integer.MIN_VALUE;break;
			}
		}else
			if(nextFloat < Operation.GEN_BASIC_VALUES + Operation.LIMITED_VALUES) 
				n= random.nextInt(21) - 10;
			else
				n= random.nextInt();
		
		return Integer.toString(n);
	}
	private static String buildLongValue() {
		float nextFloat = random.nextFloat();
		long n=0;
		if(nextFloat < Operation.GEN_BASIC_VALUES) switch(random.nextInt(5)) {
		case 0:
			n= 0L;
		case 1:
			n= 1L;
		case 2:
			n= -1L;
		case 3:
			n= Long.MAX_VALUE;
		case 4:
			n= Long.MIN_VALUE;
		}

		if(nextFloat < Operation.GEN_BASIC_VALUES + Operation.LIMITED_VALUES) 
			n= (long) random.nextInt(21) - 10;
		else
			n= random.nextLong();
		
		return Long.toString(n);
		
	}

	public static String buildStringValue(String type) {
		String str;
		StringGenerator stringGenerator = new StringGenerator();
			str = stringGenerator.newString(GAConfig.SET_TO_NULL);
		if(str==null)
			return null;
		return str;
		//return "\"" + str + "\"";
	}

	public static String buildCharacterValue() {
		return ""+characterGenerator.getCharacter();
		
	}
	 public static String buildFloatValue(String type) {

		    float n=0; 	
		    float nextFloat = random.nextFloat();

			if(nextFloat < Operation.GEN_BASIC_VALUES) switch(random.nextInt(10)) {
			case 0:
				n=  0.0f;break;
			case 1:
				n=  1.0f;break;
			case 2:
				n=  -1.0f;break;
			case 3:
				n=  Float.MAX_VALUE;break;
			case 4:
				n=   -1 * Float.MAX_VALUE;break;
			case 5:
				n=   Float.MIN_VALUE; break;
			case 6:
				n=  -1 * Float.MIN_VALUE;break;
			case 7:
				n=  Float.POSITIVE_INFINITY;break;
			case 8:
				n=  Float.NEGATIVE_INFINITY;break;
			case 9:
				n= Float.NaN;break;
			}
			else
			if(nextFloat < Operation.GEN_BASIC_VALUES + Operation.LIMITED_VALUES) 
				n=	(random.nextBoolean() ? 1 : -1) * random.nextFloat();
			else
				n= (random.nextBoolean() ? 1 : -1) * random.nextFloat() * Float.MAX_VALUE;
		    return ""+n;
		  }
	 public  static String buildDoubleValue(String type) {
		    float nextFloat = random.nextFloat();

			if(nextFloat < Operation.GEN_BASIC_VALUES) switch(random.nextInt(10)) {
			case 0:
				return ""+0.0f;
			case 1:
				return ""+1.0f;
			case 2:
				return ""+-1.0f;
			case 3:
				return ""+Double.MAX_VALUE;
			case 4:
				return ""+(-1 * Double.MAX_VALUE);
			case 5:
				return ""+Double.MIN_VALUE; // number closest to 0+
			case 6:
				return ""+(-1 * Double.MIN_VALUE); // number closest to 0-
			case 7:
				return ""+Double.POSITIVE_INFINITY;
			case 8:
				return ""+Double.NEGATIVE_INFINITY;
			case 9:
				return ""+Double.NaN;
			}

			if(nextFloat < Operation.GEN_BASIC_VALUES + Operation.LIMITED_VALUES)
				return ""+(random.nextBoolean() ? 1 : -1) * random.nextDouble();

			return ""+(random.nextBoolean() ? 1 : -1) * random.nextDouble() * Double.MAX_VALUE;
		}

	 public static boolean isNumeric(String str)
	 {
		  try  
		  {  
		///	parseInt(str)
		    int d = Integer.parseInt(str);  
		  }  
		  catch(NumberFormatException nfe)  
		  {  
		    return false;  
		  }  
		  return true;  

	   //  return str.matches("[+-]?\\d*(\\.\\d+)?");
	 }
	 public static String stringValueMuteated(String actuallValue,String pSuggestValue){
	
		if(isNumeric(pSuggestValue)&&ValuesGenerator.getBoleean(0.5f))
			pSuggestValue=""+(char) Integer.parseInt(pSuggestValue);;
		 if(actuallValue==null){
	//		 System.out.println("ValuesGenerator.string ValueMuteated: actuallValue=null");
			 actuallValue="";
		 }
		 if(pSuggestValue.trim().length()==0 &&actuallValue.trim().length()==0){
			 return ValuesGenerator.buildStringValue("");
		 }
		 if(pSuggestValue.equals(actuallValue))
			 pSuggestValue=ValuesGenerator.buildCharacterValue();
			/** probability to add a char before the selected char */
			 float probAddChar = 0.5f;

			/** probability to remove the selected char */
			 float probRemoveChar = 0.2f;

			/**  probability to modify the selected char  */
			 float probModifyChar = 0.3f;
			 String newValue=null;
		 if(pSuggestValue==null||pSuggestValue.equals("null"))
			 return newValue;
		 else
			 newValue=pSuggestValue.toString();

		 final boolean isModifiable = actuallValue.length() > 0  ;//&& (methodsRecommend.containsKey("init"));

		final boolean isRemovable = actuallValue.length()>0;
		 
		 int choice;
		 if(isModifiable && isRemovable) {
			float c = ValuesGenerator.getRandomFloat() * (probAddChar + probRemoveChar + probModifyChar);
			if(c < probAddChar) choice = 0;
			else if(c < probAddChar + probModifyChar) choice = 1;
			else choice = 2;
		} else if(isModifiable) {
			float c = ValuesGenerator.getRandomFloat() * (probAddChar + probModifyChar);
			if(c < probAddChar) choice = 0;
			else choice = 1;
		} else if(isRemovable) {
			float c = ValuesGenerator.getRandomFloat()* (probAddChar + probRemoveChar);
			if(c < probAddChar) choice = 0;
			else choice = 2;
		} else choice = 0;
		 int postition=0;
		 if(actuallValue.length()!=0)
			 	postition=ValuesGenerator.getRandomInt(actuallValue.length());
		 
		if(choice==0)// add a suggested value to the string
			newValue = new StringBuffer(actuallValue).insert(postition, pSuggestValue).toString();
		if(choice==1)//modify a char
			newValue = actuallValue.substring(0,postition)+ pSuggestValue  +actuallValue.substring(postition+1);
		if(choice==2)//remove a char  str.substring(0, idx) + str.substring(idx + 1);
			newValue = actuallValue.substring(0,postition)+ actuallValue.substring(postition+1);
		
		return newValue;
	 }
	 public  static Object mapValueToObject(Class<?> valType,String val) {
			Object obj = null;
			if(val==null||val.equals("null"))
				return obj;
			try{
				if (valType ==  java.lang.String.class)
					obj = new java.lang.String(val.trim());
				else if (valType ==  java.lang.Integer.class||valType == int.class)
					obj = new Integer(Integer.parseInt(val.trim()));
				else if (valType ==  java.lang.Long.class||valType == Long.class)
					obj = new Long(Long.parseLong(val.trim()));
				else if (valType ==  java.lang.Float.class|| valType ==float.class)
					obj = new Float(Float.parseFloat(val.trim()));
				else if (valType ==  java.lang.Double.class|| valType ==double.class)
					obj = new Double(Double.parseDouble(val.trim()));
				else if (valType ==  java.lang.Boolean.class|| valType ==boolean.class)
					obj = new Boolean(Boolean.parseBoolean(val.trim()));
				else if (valType ==  java.lang.Character.class|| valType ==char.class)
					obj = new Character((char) val.charAt(0));
//				else System.out.println("Error: ** ReflectionExecutor.mapValueToObject **"+ valType);
			}catch(Throwable e){
				System.out.println("Error: ** ReflectionExecutor.mapValueToObject **"+ valType);
				System.exit(1);
			}


			return obj;
		}
		public static String modifyActualValue(String origValue,String pValue,String pType) {
			if (pValue==null)
					return ValuesGenerator.buildValue(pType);

			Class<?> ClazzType=Filter.getPermitiveClass(pType);

			if(ClazzType== Integer.class){
				String newValue=""+((Integer)ValuesGenerator.mapValueToObject(ClazzType,origValue) +(Integer)ValuesGenerator.mapValueToObject(ClazzType,pValue) );
				return ""+((Integer)ValuesGenerator.mapValueToObject(ClazzType,newValue) + ValuesGenerator.getRandomIntBetween(-1, 1));
			}
			if(ClazzType== Long.class){
				String newValue=""+((Long)ValuesGenerator.mapValueToObject(ClazzType,origValue) +(Long)ValuesGenerator.mapValueToObject(ClazzType,pValue) );
				return ""+((Long)ValuesGenerator.mapValueToObject(ClazzType,newValue) + ValuesGenerator.getRandomIntBetween(-1, 1));
			}

			if(ClazzType== Double.class){
				String newValue=""+((Double)ValuesGenerator.mapValueToObject(ClazzType,origValue) +(Double)ValuesGenerator.mapValueToObject(ClazzType,pValue) );
				return ""+((Double)ValuesGenerator.mapValueToObject(ClazzType,newValue) + ValuesGenerator.getRandomIntBetween(-1, 1));
			}
			if(ClazzType== Float.class){
				String newValue=""+((Float)ValuesGenerator.mapValueToObject(ClazzType,origValue) +(Float)ValuesGenerator.mapValueToObject(ClazzType,pValue) );
				return ""+((Float)ValuesGenerator.mapValueToObject(ClazzType,newValue) + ValuesGenerator.getRandomIntBetween(-1, 1));
			}
			if(ClazzType== Boolean.class) 
				return ""+buildBoolValue(pType);

			if(ClazzType== java.lang.Character.class||ClazzType== char.class){ 
				if(isNumeric(pValue)&&ValuesGenerator.getBoleean(0.5f))
					return ""+ new Character((char) Integer.parseInt(pValue)); 
				else
					return pValue;
			}
			if(ClazzType== String.class){				
				String newValue=ValuesGenerator.stringValueMuteated(origValue,pValue);
				if( newValue!=null)
					return ""+(String)newValue;
			}
			else{
				System.out.println("Error: ** ReflectionExecutor.modifyActualValue **"+ pType);
				System.exit(1);
			}
			
			return null;
			
		}

	 public static void main(String args[]) {
		 ValuesGenerator.resetSeed(10);
		    while(true)
		      System.out.println(getBoleean(.9f));
		    
		  }
}